package core

import (
	"errors"
	"math"
	"openzbox.com/protocol/modbus"
	"openzbox.com/utils/uuid"
)

type ParserRule struct {
	Id      int    `json:"id"`
	Numtype string `json:"numtype"`
	Name    string `json:"name"`
}

type Poll struct {
	Id           string       `json:"id"`
	CmdName      string       `json:"cmd_name"`
	Slaveid      uint8        `json:"slaveid"`
	PortName     string       `json:"port_name"`
	PortType     string       `json:"port_type"`
	Type         string       `json:"type"`
	DeviceName   string       `json:"dev_name,omitempty"`
	Desc         string       `json:"desc"`
	Address      uint16       `json:"address"`
	Quantity     uint16       `json:"quantity"`
	FuncCode     uint8        `json:"funcCode"`
	ByteOrder    string       `json:"byteOrder"`
	Parser_rules []ParserRule `json:"parser_rules,omitempty"`
	/*Send     struct {
		Func     interface{} `json:"func"`
		Address  interface{} `json:"address,omitempty"`
		Quantity int         `json:"quantity"`
		Regaddr  int         `json:"regaddr,omitempty"`
	} `json:"send"`
	Response struct {
	} `json:"response"`*/
}

func (box *Mbox) SendPoll(poll Poll) ([]byte, error) {

	port, err := box.GetPort(poll.PortName)
	if err != nil {
		return nil, err
	}
	//x, err := json.Marshal(port.GetOpts())
	//log.Infof("%s", x)

	result := []byte{}
	p := modbus.NewRTUClientProvider(modbus.WithEnableLogger(),
		modbus.WithSerialConfig(port.GetOpts()))
	/*serial.Config{
		Address:  "/dev/ttyS7",
		BaudRate: 115200,
		DataBits: 8,
		StopBits: 1,
		Parity:   "N",
		Timeout:  modbus.SerialDefaultTimeout,
	}))*/
	//var rtx modbus.RTX = {}
	client := modbus.NewClient(p)
	err = client.Connect()
	if err != nil {
		//fmt.Println("connect failed, ", err)
		return result, err
	}
	defer client.Close()
	//log.Info(poll)
	//_, err := client.ReadHoldingRegisters(1, 0x00, 10)
	switch poll.FuncCode {
	case modbus.FuncCodeReadCoils: //1
		result, err = client.ReadCoils(poll.Slaveid, poll.Address, poll.Quantity)
		break
	case modbus.FuncCodeReadDiscreteInputs: //2
		result, err = client.ReadDiscreteInputs(poll.Slaveid, poll.Address, poll.Quantity)
		break
	case modbus.FuncCodeReadHoldingRegisters: //3
		result, err = client.ReadHoldingRegistersBytes(poll.Slaveid, poll.Address, poll.Quantity)
		break
	case modbus.FuncCodeReadInputRegisters:
		result, err = client.ReadInputRegistersBytes(poll.Slaveid, poll.Address, poll.Quantity)
		break
	case modbus.FuncCodeWriteSingleCoil:
		//OFF 01 05 00 01 00 00 9c 0a
		//ON  01 05 00 01 ff 00 dd fa
		result, err = client.WriteSingleCoil(poll.Slaveid, poll.Address, true)
		break
	case modbus.FuncCodeWriteSingleRegister:
		result, err = client.WriteSingleRegister(poll.Slaveid, poll.Address, 16)
		break
	case modbus.FuncCodeWriteMultipleCoils:
		//01 0f 00 01 00 08 01 00 c3 55
		//01 0f 00 01 00 08 01 ff 83 15
		var x = int(math.Ceil(float64(poll.Quantity) / 8.0))
		var wbytes = []byte{}
		for i := 0; i < x; i++ {
			wbytes = append(wbytes, 0xff)
		}
		result, err = client.WriteMultipleCoils(poll.Slaveid, poll.Address, poll.Quantity, wbytes)
		break
	case modbus.FuncCodeWriteMultipleRegisters:
		result, err = client.WriteMultipleRegisters(poll.Slaveid, poll.Address, poll.Quantity, []uint16{0x01})
		break
	case modbus.FuncCodeReadWriteMultipleRegisters: //23
		//err = client.ReadWriteMultipleRegisters(poll.Slaveid, poll.Address, poll.Quantity, []uint16{0x01})
		break
	case modbus.FuncCodeReadFIFOQueue: //24
		result, err = client.ReadFIFOQueue(poll.Slaveid, poll.Address)
		break
	case modbus.FuncCodeMaskWriteRegister: //22
		err = client.MaskWriteRegister(poll.Slaveid, poll.Address, uint16(1), uint16(1))
		break
	default:
		err = errors.New("未知指令:" + string(poll.FuncCode))
	}

	/*if err != nil {
		//fmt.Println(err.Error())
	}
		FuncCodeOtherReportSlaveID         = 17
	*/
	//log.Info(result)
	//fmt.Printf("ReadDiscreteInputs %#v\r\n", results)
	//time.Sleep(time.Second * 2)
	//fmt.Printf("%2x \r\n", result)
	return result, err

}

/*获取所有指令，指令名称作为关键字*/
func (box *Mbox) ListAllCommands() map[string]Poll {
	return box.Commands
}

/*获取特定指令*/
func (box *Mbox) GetCommand(cmd_name string) Poll {
	return box.Commands[cmd_name]
}

/*增加*/
func (box *Mbox) AddCommand(poll Poll) error {
	if _, ok := box.Commands[poll.CmdName]; ok {
		return errors.New("对象名称不能重复.")
	} else {
		box.Commands[poll.CmdName] = poll
		_, err := box.SaveConfig()
		return err
	}
	return nil
}

/*删除*/
func (box *Mbox) DeleteCommand(poll []Poll) error {
	for _, element := range poll {
		delete(box.Commands, element.CmdName)
	}
	_, err := box.SaveConfig()
	return err
}

/*修改*/
func (box *Mbox) UpdateCommand(poll Poll) error {
	if _, ok := box.Commands[poll.CmdName]; ok {
		poll.Id = uuid.Must(uuid.NewV4()).String()
		box.Commands[poll.CmdName] = poll
		_, err := box.SaveConfig()
		return err
	} else {
		return errors.New("对象名称不能重复.")
	}
	return nil
}
